package org.ccay.web.support.filter;

import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Calendar;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.logging.log4j.ThreadContext;
import org.apache.shiro.SecurityUtils;
import org.ccay.core.dispatcher.EventUtil;
import org.ccay.core.exception.ExceptionHandler;
import org.ccay.core.log.CcayLoggerFactory;
import org.ccay.core.log.ILogger;
import org.ccay.core.request.Application;
import org.ccay.core.request.IApplicationPrincipal;
import org.ccay.core.request.IRequestContext;
import org.ccay.core.request.IUserPrincipal;
import org.ccay.core.request.impl.RequestContextManager;
import org.ccay.core.support.session.IAfterRequestHandler;
import org.ccay.web.support.internal.impl.RequestContextInitUtil;
import org.ccay.web.support.internal.impl.RequestLangUtil;
import org.ccay.web.support.internal.impl.RequestUtil;
import org.ccay.web.support.servlet.util.ServletExceptionHandler;

/**
 * 请求上下文过滤器	
 */
public final class RequestContextFilter implements Filter {
	private static final ILogger logger = CcayLoggerFactory.getLogger(RequestContextFilter.class);
	public void destroy() {
	}

	public void doFilter(ServletRequest req, ServletResponse resp,
			FilterChain chain) throws IOException,
			ServletException {		
		long time = System.currentTimeMillis();		
		HttpServletRequest httpReq = (HttpServletRequest)req;
		if(!RequestUtil.isContextPathInitialized()){
			initContext(httpReq);
		}
		//设置请求类型
		RequestUtil.initRequestAttributes(httpReq);
		
		try{
			//设置基本的上下文，便于后续使用
			RequestContextManager.setCurrent(RequestContextInitUtil.buildBasicContext(httpReq));
			//设置当前用户
			IUserPrincipal user = (IUserPrincipal)SecurityUtils.getSubject().getPrincipal();
			RequestContextManager.getCurrent(false).setUser(user);
			//设置线程名称
			initThreadName();
			
//			//触发请求前事件
//			if(!RequestUtil.isResource(httpReq)){
//				EventUtil.dispatch(IBeforeRequestHandler.class);
//			}
			
			chain.doFilter(req, resp);
			//登陆后设置语言
			RequestLangUtil.getUserLang(httpReq);
		} catch(Exception ex){			
			//处理所有异常
 			ServletExceptionHandler.handle(ex, httpReq,(HttpServletResponse)resp);			
		} catch(Error ex){
			//记录Error但不处理
			logger.fatal(ex);
			throw ex;
		} finally{
			//触发请求后事件
			EventUtil.dispatch(IAfterRequestHandler.class);
			RequestContextManager.removeCurrent();
			
			//TODO SecurityConstants
//			if(httpReq.getHeader(SecurityConstants.INTERNAL_TOKEN) != null){
//				logger.debug("Internal invoke. Session will be cleared");
//				RequestUtil.clearSession(httpReq);
//			}
			
			time = System.currentTimeMillis() - time;
			//打印请求处理时间
			logger.debug("----["+  time+"] ms used for:[" +RequestUtil.getShortUri(httpReq) + "]");
			//还原线程名称
			Thread currentThread = Thread.currentThread();
			currentThread.setName("WebContainer : " + currentThread.getId());
		}
	}
	
	
	private void initThreadName() {
		StringBuilder name = new StringBuilder();
		name.append("WebContainer : ");
		name.append(RequestUtil.getHostName());
		name.append("-");
		
		IRequestContext rc = RequestContextManager.getCurrent(true);
		
		name.append(rc != null && rc.getUser() != null ? rc.getUser().getUserName() : "Anonymous");
		name.append("-");
		
		Calendar now = Calendar.getInstance();  
		name.append(now.get(Calendar.DAY_OF_MONTH));
		name.append("-");
		name.append(now.get(Calendar.HOUR_OF_DAY));
		name.append("-");
		name.append(now.get(Calendar.MINUTE));
		name.append("-");
		name.append(now.get(Calendar.SECOND));
		name.append("-");
		name.append(now.get(Calendar.MILLISECOND));
		name.append("-");
		Thread currentThread = Thread.currentThread();
		name.append(currentThread.getId());		
		currentThread.setName(name.toString());
		
		//设置账号便于Web Trace功能过滤器跟踪用户日志
		ThreadContext.put("userName", (rc != null && rc.getUser() != null ? rc.getUser().getUserName() : "Anonymous"));
	}
	
	/** 
	 * 初始化应用上下文，因为Servlet2.3 API无法从Listener中获取contextPath，只能采取此笨方法。
	 * 
	 * TODO 在Servlet版本升级后，此方法中的获取contextpath部分可以去除
	 * @param httpReq
	 */
	private void initContext(HttpServletRequest httpReq) {
		String contextPath = RequestUtil.getContextPath(httpReq);
		IApplicationPrincipal app = Application.getCurrent();
		((Application)app).setContextPath(contextPath);
		logger.info("After context path set to: "+ contextPath);
		try {
			RequestUtil.setHostName(InetAddress.getLocalHost().getHostName());
		} catch (UnknownHostException e) {
			ExceptionHandler.logException(e);
			RequestUtil.setHostName(httpReq.getLocalName());
		}
		RequestUtil.setContextPathInitialized(true);
	}

	public void init(FilterConfig arg0) throws ServletException {
		
	}
}
